#include "Gui.h"
#include <stdlib.h>

Gui* Gui::singleton = 0;
const int Gui::maxNumDataRecords = 10;
const int Gui::lineBeginningOfData = 5;
const int Gui::infoLine = 2;
const int Gui::nodeLine = 3;
const int Gui::lineReply = 15;
const int Gui::lineLegend = 12;
const int Gui::lineInput = 19;
const int Gui::refreshPeriod = 3000; //ms
int Gui::numFields = 0;
//bool Gui::toggleInfo=true;

using namespace std;

void Gui::display(const unsigned int len, const void* msg) {
	if(len == 0 || msg == 0) return;
	lastDataInput.first = len;
	lastDataInput.second = const_cast<void*>(msg);

	void* helpPointer = const_cast<void*>(msg);
	//Explanation of this part:
	//For every data field to be displayed the pointer is casted to the type of the expected data field
	//and then printed. Afterwards the casted(!) pointer is incremented by one so it points to the beginning
	//of the next data field.
	for(int i = 0; i < numFields; ++i) {
		movePointer(i);
		switch(description.type[i]) {
		case DTCHAR:
			PRINTF("%c", *((char*)helpPointer));
			helpPointer = ((char*)helpPointer)+1;
			break;
		case DTBOOL:
			PRINTF( *((bool*)helpPointer) ? "true" : "false");
			helpPointer = ((bool*)helpPointer)+1;
			break;
		case DTSHORT:
			PRINTF("%d", *((short*)helpPointer));
			helpPointer = ((short*)helpPointer)+1;
			break;
		case DTLONG:
			PRINTF("%ld", *((long*)helpPointer));
			helpPointer = ((long*)helpPointer)+1;
			break;
		case DTLONGLONG:
			PRINTF("%lld", *((long long*)helpPointer));
			helpPointer = ((long long*)helpPointer)+1;
			break;
		case DTFLOAT:
			PRINTF("%g", *((float*)helpPointer));
			helpPointer = ((float*)helpPointer)+1;
			break;
		case DTDOUBLE:
			PRINTF("%g", *((double*)helpPointer));
			helpPointer = ((double*)helpPointer)+1;
			break;
		case DTSTRING:
			PRINTF("%s", *((char**)helpPointer));
			helpPointer = ((char**)helpPointer)+1;
			break;
		default:
			break;
		}
	}
}

	//Moves the Pointer to the needed position for displaying a specific data field
	//index tells the index of the dataField-array that describes the data field
void Gui::movePointer(int index) {
	PRINTF(SCREEN_MOVE_CURSOR_TO, lineBeginningOfData+index, strlen(description.names[index])+3);
}


Gui::Gui(DataDescription dd) : Thread("GUI"), wStatusSubscriber(satelliteStatus,wStatusBuffer) {
	if(!singleton) {
		singleton = this;
	}
	singleton->description = dd;
	for(;numFields<16 && dd.names[numFields] != 0;++numFields);
	satelliteInterface = new SatelliteInterface();
	//PRINTF("%d", numFields);
}

void Gui::init(void) {
	if(this != singleton) return;
	this->setPeriodicBeat(NOW()+refreshPeriod*MILLISECONDS, refreshPeriod*MILLISECONDS);
	srandom(NOW());
	toggleInfo=true;
	//testing part
	int nodes[] = {1,8,5,3,7};
	//this->setNodeList(5,nodes);
	//this->setWorker(5);
	this->setInfoMessage("Wir zeigen unten Zeug an, jetzt ist der Teil hier dran.");

}

void Gui::run(void) {
	StatusData temp;
	int counter = 0;
	
	while(1) {
		singleton->refresh();
		
		//getStatusData();
		wStatusBuffer.get(temp);
		this->setStatus(sizeof(StatusData),(void*)&temp);

		if (counter % 3 == 0) {
			satelliteInterface->updateSatellite(); 
			// SatelliteInterface ausfuehren
			numNodes = satelliteInterface->getNumNodes();
			numDisabledNodes = satelliteInterface->getNumDisabledNodes();
			numResponsiveNodes = satelliteInterface->getNumResponsiveNodes();
			workerID = satelliteInterface->getWorkerID();
			this->setWorker(workerID);
			nodeIDList = satelliteInterface->getNodeIDList();
			this->setNodeList(numNodes,nodeIDList);
		}
		counter = counter % 3 + 1;

		waitUntilNextBeat();
	}
}

//Inherited from Putter, needs pointer to data struct
void Gui::setStatus(const unsigned int len, const void* msg) {
	singleton->display(len, msg);
	singleton->lastDataInput.first = len;
	singleton->lastDataInput.second = const_cast<void*>(msg);
}

//void Gui::getStatusData() {
//	StatusData statusTest;
//	wStatusBuffer.get(statusTest);
//	if (statusTest.posX != 0) {
//		statusData = statusTest;
//		PRINTF("TEST:%g \n", statusData.posX);
//	}
//}

void Gui::setInfoMessage(const char* msg) {
	singleton->statusMessage = msg;
	//It will be printed the next time refresh() is called
}

void Gui::setNodeList(int len, int* IDs) {
	singleton->nodeList.clear();
	for(int i = 0; i < len; ++i) {
		singleton->nodeList.push_back(IDs[i]);
	}
}

void Gui::setWorker(int ID) {
	singleton->workerID = ID;
}

void Gui::setConsoleInput(const char* msg){
	singleton->consoleInput = msg;
}

void Gui::setReply(const char* msg){
	singleton->reply = msg;
}

void Gui::setToggleInfo(){
	singleton->toggleInfo= !singleton->toggleInfo;
}

	//Redraws the interface
void Gui::refresh() {
	PRINTF(SCREEN_CLEAR);
	PRINTF(SCREEN_MOVE_CURSOR_TO,1,1);
	PRINTF("EGSE\n");
	PRINTF(SCREEN_MOVE_CURSOR_TO,infoLine,1);
	if(!singleton->statusMessage.empty()) PRINTF("%s\n", singleton->statusMessage.c_str());
	PRINTF(SCREEN_MOVE_CURSOR_TO, nodeLine, 1);
	PRINTF("Active nodes: ");
	for(unsigned int i = 0; i < singleton->nodeList.size(); ++i) {
		if(!(singleton->nodeList[i] == workerID)) {
			PRINTF( (i<singleton->nodeList.size()-1) ? "%d, " : "%d",
					singleton->nodeList[i]);
		}
		else PRINTF((i<singleton->nodeList.size()-1) ? "%d(*), " : "%d(*)",
					singleton->nodeList[i]);
	}
	PRINTF(SCREEN_MOVE_CURSOR_TO, lineBeginningOfData, 1);
	for(int i = 0; i < numFields; ++i) {
		PRINTF("%s: \n", singleton->description.names[i]);
	}
	PRINTF("Nodes: %i \n", numNodes);
	PRINTF("Disabled Nodes: %i \n", numDisabledNodes);
	PRINTF("Responsive Nodes: %i \n", numResponsiveNodes);

	PRINTF(SCREEN_MOVE_CURSOR_TO, lineReply);
	PRINTF("%s\n",singleton->reply.c_str());
	PRINTF(SCREEN_MOVE_CURSOR_TO, lineInput);
	PRINTF("%s\n",singleton->consoleInput.c_str());
	if(toggleInfo){
		PRINTF(SCREEN_MOVE_CURSOR_TO, lineLegend);
		PRINTF("Hier steht der Infoteil");
	}


	singleton->display(singleton->lastDataInput.first, singleton->lastDataInput.second);
	PRINTF(SCREEN_MOVE_CURSOR_TO,lineInput,1);
}

Gui* Gui::getSingletonPtr(){
	return singleton;
}
